package org.ns.summer.beans.beanprocessor;

import org.ns.summer.aop.AopUtils;
import org.ns.summer.beans.*;
import org.ns.summer.beans.anns.*;
import org.ns.summer.beans.definition.BeanDefinition;
import org.ns.summer.beans.definition.DefaultBeanDefinition;
import org.ns.summer.beans.error.BeansException;
import org.ns.summer.core.AnnotationUtils;
import org.ns.summer.core.MethodUtils;

import java.lang.reflect.Method;
import java.util.function.Supplier;

public class ConfigurationClassPostProcessor implements BeanFactoryPostProcessor, InitializingBeanPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableBeanFactory beanFactory) throws BeansException {
        importConfig(beanFactory);
        beanConfig(beanFactory);
    }

    @Override
    public void preHandler(ConfigurableBeanFactory beanFactory, Object bean, String beanName) throws BeansException {

    }


    private void importConfig(ConfigurableBeanFactory beanFactory) {

        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);
            Class<?> beanType = beanDefinition.getBeanClass();
            if (AnnotationUtils.existAnnotation(beanType, Import.class)) {
                Import anImport = (Import)AnnotationUtils.getAnnotation(beanType, Import.class);
                handlerimport(beanFactory, anImport);
            } else if (AnnotationUtils.existAnnotation(beanType, Imports.class)) {
                Imports anImports = (Imports)AnnotationUtils.getAnnotation(beanType, Imports.class);
                for (Import anImport : anImports.value()) {
                    handlerimport(beanFactory, anImport);
                }
            }
        }
    }
    private void handlerimport(ConfigurableBeanFactory beanFactory, Import anImport) {
        for (Class<?> aClass : anImport.value()) {

            Component component = (Component)AnnotationUtils.getAnnotation(aClass, Component.class);
            String name = ComponentUtils.getBeanName(aClass.getName(), component);
            if (beanFactory.containsBean(name)) {
                return;
            }
            DefaultBeanDefinition beanDefinition = new DefaultBeanDefinition();
            beanDefinition.setBeanClassName(aClass.getName());
            beanDefinition.setClassLoader(beanFactory.getBeanClassLoader());
            beanDefinition.setBeanClass(aClass);
            beanFactory.registerBeanDefinition(name, beanDefinition);
        }
    }
//java.util.function.Supplier
    private void beanConfig(ConfigurableBeanFactory beanFactory) {
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (String declaringBeanName : beanDefinitionNames) {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(declaringBeanName);
            if (MethodUtils.existMethodByAnnotation(beanDefinition.getBeanClass(), Bean.class)) {
                beanConfig(beanFactory, beanDefinition, declaringBeanName);
            }
        }
    }
    private void beanConfig(ConfigurableBeanFactory beanFactory, BeanDefinition beanDefinition, String declaringBeanName) {
        try {


            Class<?> beanType = beanDefinition.getBeanClass();
            Class<?> targetType = AopUtils.getTargetType(beanType);
            Method[] declaredMethods = targetType.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
                beanConfig(beanFactory, declaringBeanName, declaredMethod);
            }
        } catch (Exception e) {

            throw e;
        }

    }

    private void beanConfig(ConfigurableBeanFactory beanFactory , String declaringBeanName , Method method) {
        if (AnnotationUtils.existAnnotation(method, Bean.class)) {
            Bean beanAnn = (Bean)AnnotationUtils.getAnnotation(method, Bean.class);
            String name = method.getName();
            if (!beanAnn.value().trim().isEmpty()) {
                name = beanAnn.value();
            }
            Class<?> beanType = method.getReturnType();
            // Object bean = BeanUtils.invoke(method, declaringBean);
            Supplier supplier =()-> {
                try {
                    method.setAccessible(true);
                    return method.invoke(beanFactory.getBean(declaringBeanName));
                } catch (Exception e) {
                    throw new BeansException(":" + method.getName(), e);
                }
            };
            DefaultBeanDefinition beanDefinition = new DefaultBeanDefinition();
            beanDefinition.setBeanClass(AnnotationFacrotyBean.class);
            beanDefinition.setBeanClassName(AnnotationFacrotyBean.class.getName());
            beanDefinition.setClassLoader(beanFactory.getBeanClassLoader());
            beanDefinition.addPropertyValue("objecttype", beanType);
            beanDefinition.addPropertyValue("object", supplier);

            beanFactory.registerBeanDefinition(name, beanDefinition);
        }
    }



    public static class AnnotationFacrotyBean implements FactoryBean{
        private Class<?> objectType ;
        private Supplier object;

        public AnnotationFacrotyBean() {
        }

        @Override
        public Class<?> getObjectType() {
            return objectType;
        }

        public void setObjectType(Class<?> objectType) {
            this.objectType = objectType;
        }

        @Override
        public Object getObject() {
            return object.get();
        }

        public void setObject(Supplier object) {
            this.object = object;
        }
    }
}
